home *** CD-ROM | disk | FTP | other *** search
/ Java Programmer's Toolkit / Java Programmer's Toolkit.iso / gs3.53 / pdf_base.ps < prev    next >
Text File  |  1996-01-10  |  12KB  |  371 lines

  1. %    Copyright (C) 1994, 1995 Aladdin Enterprises.  All rights reserved.
  2.  
  3. % pdf_base.ps
  4. % Basic parser for PDF reader.
  5.  
  6. % This handles basic parsing of the file (including the trailer
  7. % and cross-reference table), as well as objects, object references,
  8. % and streams; it doesn't include any facilities for making marks on
  9. % the page.
  10.  
  11. /.setlanguagelevel where { pop 2 .setlanguagelevel } if
  12. .currentglobal true .setglobal
  13. /pdfdict where { pop } { /pdfdict 100 dict def } ifelse
  14. pdfdict begin
  15.  
  16. % Since PDF files can include extremely large dictionaries,
  17. % we increase the operand stack size here.
  18. /setuserparams where
  19.  { pop mark /MaxOpStack 10000 .dicttomark setuserparams
  20.  } if
  21.  
  22. % We rebind #, #?, #dsc, and #dscfile later if we're writing out PostScript.
  23. /#            % <arg1> ... <argN> <opname> <N> # -
  24.  { pop cvx exec
  25.  } bind def
  26. /#?
  27.  { false
  28.  } bind def
  29. /#dsc            % mark <obj1> ... #dsc -
  30.  { cleartomark
  31.  } bind def
  32. /#dscfile        % <filename> #dscfile -
  33.  { pop
  34.  } bind def
  35.  
  36. % Define the name interpretation dictionary for reading values.
  37. /valueopdict mark
  38.   (<<) cvn { mark } bind    % don't push an actual mark!
  39.   (>>) cvn /.dicttomark load
  40.   /[ { mark } bind        % ditto
  41.   /] /] load
  42.   /true true
  43.   /false false
  44.   /null null
  45.   /F dup cvx        % see Objects section below
  46.   /R dup cvx        % see Objects section below
  47.   /stream dup cvx    % see Streams section below
  48. .dicttomark readonly def
  49.  
  50. % ------ Utilities ------ %
  51.  
  52. % Define a scratch string.  The PDF language definition says that
  53. % no line in a PDF file can exceed 255 characters.
  54. /pdfstring 255 string def
  55.  
  56. % Read the previous line of a file.  If we aren't at a line boundary,
  57. % read the line containing the current position.
  58. /prevline        % - prevline <startpos> <substring>
  59.  { PDFfile fileposition pdfstring
  60.    1 index 257 sub 0 .max PDFfile exch setfileposition
  61.     { PDFfile fileposition
  62.       PDFfile 2 index readline pop
  63.         % Stack: initpos string startpos substring
  64.       PDFfile fileposition 4 index ge { exit } if
  65.       pop pop
  66.     }
  67.    loop 4 2 roll pop pop
  68.  } bind def
  69.  
  70. % Execute a file, interpreting its executable names in a given
  71. % dictionary.  The name procedures may do whatever they want
  72. % to the operand stack.
  73. /.pdfrun            % <file> <opdict> .pdfrun -
  74.  {    % Construct a procedure with the file and opdict bound into it.
  75.    1 index cvlit mark mark 4 2 roll
  76.     { token not { (%%EOF) cvn cvx } if
  77.       dup xcheck
  78.        { DEBUG { dup == flush } if
  79.      2 copy .knownget
  80.       { exch pop exch pop exec }
  81.       { (%stderr) (w) file
  82.         dup (****************Unknown operator: ) writestring
  83.         dup 3 -1 roll .writecvs dup (\n) writestring flushfile
  84.         pop
  85.       }
  86.      ifelse
  87.        }
  88.        { exch pop DEBUG { dup ==only ( ) print flush } if
  89.        }
  90.       ifelse
  91.     }
  92.    aload pop .packtomark cvx
  93.    /loop cvx 2 packedarray cvx
  94.     { stopped /PDFsource } aload pop
  95.    PDFsource
  96.     { store { stop } if } aload pop .packtomark cvx
  97.    /PDFsource 3 -1 roll store exec
  98.  } bind def
  99.  
  100. % ------ File reading ------ %
  101.  
  102. % Read the cross-reference entry for an (unresolved) object.
  103. % The caller must save and restore the PDFfile position if desired.
  104. % For invalid (free) objects, we return 0.
  105. /readxrefentry        % <object#> readxrefentry <objpos>
  106.  { dup Objects exch get
  107.    PDFfile exch setfileposition
  108.    PDFfile token pop        % object position
  109.    PDFfile token pop        % generation #
  110.    PDFfile token pop        % n or f
  111.    dup /n eq
  112.     { pop 1 add dup 255 gt
  113.        { Generations type /stringtype eq
  114.       {        % Convert Generations from a string to an array.
  115.         Generations length array dup
  116.         0 1 2 index length 1 sub
  117.          { Generations 1 index get put dup
  118.          }
  119.         for pop /Generations exch store
  120.       }
  121.      if
  122.        }
  123.       if
  124.     }
  125.     { /f eq
  126.        { pop 0 }
  127.        { /readxrefentry cvx /syntaxerror signalerror }
  128.       ifelse
  129.     }
  130.    ifelse
  131.         % Stack: obj# objpos 1+gen#
  132.    Generations 4 -1 roll 3 -1 roll put
  133.  } bind def
  134.  
  135. % ================================ Objects ================================ %
  136.  
  137. % We represent an unresolved object reference by a procedure of the form
  138. % {obj# gen# resolveR}.  This is not a possible PDF object, because PDF has
  139. % no way to represent procedures.  Since PDF in fact has no way to represent
  140. % any PostScript object that doesn't evaluate to itself, we can 'force'
  141. % a possibly indirect object painlessly with 'exec'.
  142. % Note that since we represent streams by executable dictionaries
  143. % (see below), we need both an xcheck and a type check to determine
  144. % whether an object has been resolved.
  145. /unresolved?        % <object#> unresolved? <bool>
  146.  { Objects exch get dup xcheck exch type /integertype eq and
  147.  } bind def
  148. /oforce /exec load def
  149. /oget        % <array> <index> oget <object>
  150.         % <dict> <key> oget <object>
  151.  { 2 copy get dup xcheck
  152.     { exec dup 4 1 roll put }
  153.     { exch pop exch pop }
  154.    ifelse
  155.  } bind def
  156. % A null value in a dictionary is equivalent to an omitted key;
  157. % we must check for this specially.
  158. /knownoget
  159.  { 2 copy known
  160.     { oget dup null eq { pop false } { true } ifelse }
  161.     { pop pop false }
  162.    ifelse
  163.  } bind def
  164.  
  165. % PDF 1.1 defines a 'foreign file reference', but not its meaning.
  166. % Per the specification, we convert these to nulls.
  167. /F        % <file#> <object#> <generation#> F <object>
  168.  {        % Some PDF 1.1 files use F as a synonym for f!
  169.    count 3 lt { setfillcolor /fill fsexec } { pop pop pop null } ifelse
  170.  } bind def
  171.  
  172. % We keep track of objects in a pair of arrays, Objects and Generations.
  173. % Generations[N] is 1+ the current generation number for object number N.
  174. % (As far as we can tell, this is needed only for error checking.)
  175. % If object N is loaded, Objects[N] is the actual object;
  176. % otherwise, Objects[N] is an executable integer giving the file offset
  177. % of the object's entry in the cross-reference table.
  178. % For free objects, Generations[N] is 0.
  179. /checkgeneration  % <object#> <generation#> checkgeneration <object#> <OK>
  180.  { Generations 2 index get 1 sub 1 index eq
  181.     { pop true
  182.     }
  183.     { (Warning: wrong generation: ) print 1 index =only ( ) print = false
  184.     }
  185.    ifelse
  186.  } bind def
  187. /R        % <object#> <generation#> R <object>
  188.  { 1 index unresolved?
  189.     { /resolveR cvx 3 packedarray cvx }
  190.     { checkgeneration { Objects exch get } { pop null } ifelse }
  191.    ifelse
  192.  } bind def
  193.  
  194. % If we encounter an object definition while reading sequentially,
  195. % we just store it away and keep going.
  196. /objopdict mark
  197.   valueopdict { } forall
  198.   /endobj dup cvx
  199. .dicttomark readonly def
  200. /obj            % <object#> <generation#> obj <object>
  201.  { PDFfile objopdict .pdfrun
  202.  } bind def
  203. /endobj            % <object#> <generation#> <object> endobj <object>
  204.  { 3 1 roll
  205.         % Read the xref entry if we haven't yet done so.
  206.         % This is only needed for generation # checking.
  207.    1 index unresolved?
  208.     { PDFfile fileposition
  209.       2 index readxrefentry pop
  210.       PDFoffset add PDFfile exch setfileposition
  211.     } if
  212.    checkgeneration { Objects exch 2 index put } { pop pop null } ifelse
  213.  } bind def
  214.  
  215. % When resolving an object reference, we stop at the endobj.
  216. /resolveopdict mark
  217.   valueopdict { } forall
  218.   /endobj { endobj exit } bind
  219. .dicttomark readonly def
  220. /resolveR        % <object#> <generation#> resolveR <object>
  221.  { DEBUG { (%Resolving: ) print 2 copy 2 array astore == } if
  222.    1 index unresolved?
  223.     { PDFfile fileposition 3 1 roll
  224.       1 index readxrefentry
  225.       3 1 roll checkgeneration
  226.        {        % Stack: savepos objpos obj#
  227.      exch PDFoffset add PDFfile exch setfileposition
  228.      PDFfile token pop 2 copy ne
  229.       { (xref error!\n) print /resolveR cvx /rangecheck signalerror
  230.       }
  231.      if pop PDFfile token pop
  232.      PDFfile token pop /obj ne
  233.       { (xref error!\n) print /resolveR cvx /rangecheck signalerror
  234.       }
  235.      if
  236.      PDFfile resolveopdict .pdfrun
  237.        }
  238.        { Objects exch null put pop null
  239.        }
  240.       ifelse exch PDFfile exch setfileposition
  241.     }
  242.     { pop Objects exch get
  243.     }
  244.    ifelse
  245.  } bind def      
  246.  
  247. %================================ Streams ================================ %
  248.  
  249. % We represent a stream by an executable dictionary that contains,
  250. % in addition to the contents of the original stream dictionary:
  251. %    /File - the file or string where the stream contents are stored;
  252. %    /FilePosition - iff File is a file, the position in the file
  253. %      where the contents start.
  254. % We do the real work of constructing the data stream only when the
  255. % contents are needed.
  256.  
  257. % Construct a stream.  The length is not reliable in the face of
  258. % different end-of-line conventions, but it's all we've got.
  259. % Since the stream keyword may be followed by 0, 1, or more blanks,
  260. % we have to back up in the file to find where the data actually starts.
  261. % The stream keyword may be followed by a blank line, which we also
  262. % must skip before reading any data.  (This is chancy if the data are
  263. % in binary form, but such files are questionable to begin with.)
  264. /streamskipeols
  265.  {  { PDFsource read not { /stream cvx /syntaxerror signalerror } if
  266.       dup 10 eq 1 index 13 eq or not { PDFsource exch unread exit } if pop
  267.     }
  268.    loop
  269.  } bind def
  270. /stream
  271.  { PDFsource PDFfile eq
  272.     { dup /File PDFfile put
  273.       prevline pop pop
  274.       streamskipeols
  275.       dup /FilePosition PDFfile fileposition put
  276.       DEBUG { (%FilePosition: ) print dup /FilePosition get == } if
  277.       PDFfile fileposition 1 index /Length oget add
  278.         PDFfile exch setfileposition
  279.     }
  280.     {    % We're already reading from a stream, which we can't reposition.
  281.     % Capture the sub-stream contents in a string.
  282.       streamskipeols
  283.       dup /Length oget string PDFsource exch readstring
  284.       not
  285.        { (Unexpected EOF in stream!\n) print
  286.      /stream cvx /rangecheck signalerror
  287.        }
  288.       if
  289.       1 index exch /File exch put
  290.     }
  291.    ifelse
  292.    PDFsource token pop
  293.      /endstream ne { /stream cvx /syntaxerror signalerror } if
  294.    cvx
  295.  } bind def
  296.  
  297. % Resolve a stream dictionary to a PostScript stream.
  298. % Streams with no filters require special handling:
  299. %    - If we are going to interpret their contents, we let endstream
  300. %      terminate the interpretation loop;
  301. %    - If we are just going to read data from them, we impose
  302. %      a SubFileDecode filter that reads just the requisite amount of data.
  303. % Note that, in general, resolving a stream repositions PDFfile.
  304. % Clients must save and restore the position of PDFfile themselves.
  305. /resolvestream        % <streamdict> <readdata?> resolvestream <stream>
  306.  { exch dup /FilePosition .knownget
  307.     { 1 index /File get exch setfileposition }
  308.    if
  309.         % Stack: readdata? dict
  310.    dup /DecodeParms .knownget not { null } if
  311.    1 index /Filter .knownget not { {} } if
  312.    dup type /nametype eq
  313.     { 1 array astore
  314.       1 index null ne { exch 1 array astore exch } if
  315.     }
  316.    if
  317.         % Stack: readdata? dict parms filternames
  318.    2 index /File get exch
  319.         % Stack: readdata? dict parms file/string filternames
  320.    dup length 0 eq
  321.     {        % All the PDF filters have EOD markers, but in this case
  322.         % there is no specified filter.
  323.       pop exch pop
  324.         % Stack: readdata? dict file/string
  325.       2 index
  326.        {    % We're going to read data; use a SubFileDecode filter.
  327.      1 index /Length oget () /SubFileDecode filter
  328.        }
  329.        { dup type /filetype ne
  330.       {    % Use a SubFileDecode filter to read from a string.
  331.         0 () SubFileDecode filter
  332.       }
  333.      if
  334.        }
  335.       ifelse
  336.     }
  337.     { 2 index null eq
  338.        { { filter }
  339.        }
  340.        {    % Stack: readdata? dict parms file/string filtername
  341.          { 2 index 0 get dup null eq { pop } { exch } ifelse filterpdf
  342.        exch dup length 1 sub 1 exch getinterval exch
  343.      }
  344.        }
  345.       ifelse forall exch pop
  346.     }
  347.    ifelse
  348.         % Stack: readdata? dict file
  349.    exch pop exch pop
  350.  } bind def
  351. /endstream { exit } def
  352.  
  353. % Construct a PDF filter.  These are the same as PostScript filters,
  354. % with one exception: LZWDecode filters with Predictor=2 must insert
  355. % a PixelDifferenceDecode filter in the pipeline.
  356. /filterpdf        % <source> <...params...> <name> filterpdf <stream>
  357.  { dup /LZWDecode eq 2 index type /dicttype eq and
  358.     { 1 index /Predictor .knownget not { 1 } if 1 sub
  359.        { { filter }
  360.      { 1 index 4 1 roll filter exch /PixelDifferenceDecode filter }
  361.        }
  362.       exch get exec
  363.     }
  364.     { filter
  365.     }
  366.    ifelse
  367.  } bind def
  368.  
  369. end            % pdfdict
  370. .setglobal
  371.